<?php

declare(strict_types=1);

namespace App\CustHub\Service;

use App\Common\Robot\Robot;
use App\CustHub\Event\SopTagTrigger;
use App\CustHub\Model\ChAddFriend;
use App\CustHub\Model\ChContact;
use App\CustHub\Constants\ContactBehaviorTypeConstants;
use App\CustHub\Constants\ContactSourceConstants;
use App\CustHub\Constants\ContactStatusConstants;
use App\CustHub\Event\NewCustomerSchemeTrigger;
use App\CustHub\Event\SmartTagRoomBehaviorTrigger;
use App\CustHub\Event\SopNewUserTrigger;
use App\CustHub\Mapper\ChContactMapper;
use App\CustHub\Mapper\ChCorpMapper;
use App\CustHub\Model\ChContactBehaviorLog;
use App\CustHub\Model\ChContactMobile;
use App\CustHub\Model\ChRobot;
use App\Robot\Service\RobotApiService;
use Carbon\Carbon;
use FriendsOfHyperf\Lock\Exception\LockTimeoutException;
use Hyperf\Coroutine\Coroutine;
use Hyperf\DbConnection\Db;
use Hyperf\Di\Annotation\Inject;
use Psr\EventDispatcher\EventDispatcherInterface;
use Psr\Log\LoggerInterface;
use function FriendsOfHyperf\Lock\lock;
use function Hyperf\Collection\collect;
use function Hyperf\Coroutine\co;
use function Hyperf\Support\optional;
use function Swoole\Coroutine\Http\request;

class ContactReceiveCallBackService
{

    private const FRIEND_APPLY_KEY = 'friend_apply_key:';

    #[Inject]
    protected ChContactMapper $contactMapper;

    #[Inject]
    protected ChCorpMapper $corpMapper;

    #[Inject]
    protected ChCorpService $corpService;

    #[Inject]
    protected ActiveAddFriendService $addFriendService;

    #[Inject]
    protected ChCustomerServiceLinkService $customerServiceLinkService;

    #[Inject]
    protected ChLockCustomerCodeService $lockCustomerService;

    #[Inject]
    private EventDispatcherInterface $eventDispatcher;

    public function handle(array $data,ChRobot $robotInfo)
    {
        stdout_log()->info('消息类型:'.$data['msg_type']);
        switch ($data['msg_type']){
            case 2131:
                //外部联系人信息变动通知，包括【备注/标签/手机号/描述/昵称】等信息
                $lock = lock('contact_update_lock:'.$robotInfo->id,4);
                try {
                    $lock->block(2);
                    $this->handleExternalContactUpdate($data,$robotInfo);
                } catch (LockTimeoutException $e) {
                    stdout_log()->error('重复操作');
                } finally {
                    $lock->release();
                }
                break;
           case 2132:
                //好友申请
                $this->handleExternalContactApply($data,$robotInfo);
                break;
            default:
                //其他类型
                break;
        }
    }

    public function messageHandler(array $data, ChRobot $robotInfo): void
    {
        $content = $data['content'];
        $sender = $data['sender'];
        stdout_log()->info('新客户消息处理:'.$robotInfo->id.'-'.$sender.'-'.$content);

        if($data['sender'] != $data['receiver'] && (str_contains($content,'我通过了你的联系人验证请求') || (str_contains($content,'你已添加了') && str_contains($content,'现在可以开始聊天了')) || str_contains($content,'我已经添加了你'))){
            stdout_log()->info('新客户运营调试:'.Carbon::now()->toDateTimeString().$robotInfo->id.'添加了新客户');
            $contact=$this->saveNewContact($data,$robotInfo,true);
            if(!empty($contact)){
                co(function ()use($data,$robotInfo,$contact){
                    sleep(3);
                    container()->get(ChChannelCodeService::class)->afterScanCode($data,$robotInfo,$contact);
                });
            }
        }
        if($this->checkIsMobile($content)){
            //新增手机号
            $contactCfg = container()->get(ChConfigService::class)->getConfigByGroupKey($robotInfo->uid,$robotInfo->main_uid,'contact_cfg');
            $autoSaveMobile = $contactCfg['auto_save_mobile']['value'];

            if($sender == $data['user_id']){
                //企微号发送的情况
                $sender = $data['receiver'];
            }
            if($autoSaveMobile == 1){
                $this->autoSaveMobile($sender,$content,$robotInfo);
            }
        }

    }

    /**
     * 检查是否是手机号
     * @param string $content
     * @return bool
     */
    private function checkIsMobile(string $content): bool
    {
        //11位数字
        $pattern = '/^1\d{10}$/';
        //手机号
        $mobilePattern = '/^((13[0-9])|(14[5,7])|(15[0-3,5-9])|(17[0,3,5-8])|(18[0-9])|166|198|199|(147))\d{8}$/';

        if(preg_match($mobilePattern, $content) === 1 || preg_match($pattern, $content) === 1){
            return true;
        }
        return false;
    }

    /**
     * 添加新客户
     * @param array $data
     * @param ChRobot $robotInfo
     * @param bool $isNew 是否新客户
     * @return mixed
     * @throws \Psr\Container\ContainerExceptionInterface
     * @throws \Psr\Container\NotFoundExceptionInterface
     */
    private function saveNewContact(array $data,ChRobot $robotInfo,bool $isNew = false)
    {
        $sender = $data['sender'];

        $lock = lock('save_new_contact_lock:'.$sender.'_'.$robotInfo->id,4);
        try {
            $lock->block(2);
            $contact = ChContact::query()
                ->where('robot_id',$robotInfo->id)
                ->where('user_id',$sender)
                ->first();
            $isFriendApply = false;
            if(empty($contact)){
                redis()->set('new_user:'.$robotInfo->id."_".$sender,1,20);
                //新增好友处理事件
                $contact = $this->saveContact($robotInfo,$sender);

                $isFriendApply = redis()->exists(self::FRIEND_APPLY_KEY.$sender);
                redis()->del(self::FRIEND_APPLY_KEY.$sender);
                $isNew = true;
            }
            //更新主动加好友状态
            $this->addFriendService->updateFriendStatus($contact);
            if($isNew){
                co(function ()use($data,$robotInfo,$sender,$contact,$isFriendApply){
                    //新客户运营触发
                    $this->eventDispatcher->dispatch(new NewCustomerSchemeTrigger($data, $robotInfo,$isFriendApply));
                    //社群行为智能标签
                    $this->eventDispatcher->dispatch(new SmartTagRoomBehaviorTrigger($robotInfo->id,$sender));
                    //触发新用户sop
                    $this->eventDispatcher->dispatch(new SopNewUserTrigger($contact));

                });
            }else{
                //已经是好友处理主动加好友
                $this->addFriendService->autoOperate($contact,false);
            }
            return $contact;
        }catch (LockTimeoutException $e){
            return null;
        }finally{
            $lock->release();
        }
    }

    public function saveContact(ChRobot $robotInfo,string $sender)
    {
        $robotApiService = make(Robot::class, [$robotInfo->server_id, $robotInfo->uuid])->gateway();
        $result = $robotApiService->getContactDetail([$sender]);
        if($result === false){
            return null;
        }

        $contactData = array_shift($result['contact_list']);
        $corpInfo = $robotApiService->getCorpInfo([$contactData['corp_id']]);
        if($corpInfo === false){
            $corpInfo = [
                'corp_name' => '微信',
                'corp_full_name' => '微信联系人',
            ];
        }else{
            $corpInfo = array_shift($corpInfo['corp_list']);
        }
        $contactData['avatar'] = $contactData['avatar_url'];
        $contactData['uid'] = $robotInfo->uid;
        $contactData['main_uid'] = $robotInfo->main_uid;
        $contactData['robot_id'] = $robotInfo->id;
        $contactData['robot_corp_id'] = $robotInfo->corp_id;
        $contactData['corp_wxid'] = $contactData['corp_id'];
        $contactData['corp_id'] = 0;
        $contactData['corp_short_name'] = $corpInfo['corp_name'];
        $contactData['corp_name'] = $corpInfo['corp_full_name'];
        $contactData['is_wechat'] = $contactData['corp_name'] == '微信联系人' ? 1 : 0;
        $contactData['add_customer_time'] = time();
        $contactData['create_time'] = time();
        $contactData['add_time'] = time();
        //状态
        $contactData['status'] = ContactStatusConstants::NORMAL_FRIEND;
        $this->contactMapper->filterExecuteAttributes($contactData);
        $contact = $this->contactMapper->model::create($contactData);
        $this->contactMapper->rmCache($robotInfo->id,$contact->user_id);
        return $contact;
    }

    /**
     * 自动保存手机号
     * @param string $sender
     * @param string $mobile
     * @param ChRobot $robotInfo
     * @return void
     */
    private function autoSaveMobile(string $sender,string $mobile,ChRobot $robotInfo): void
    {
        $lock = lock('auto_save_mobile:'.md5($robotInfo->id.$sender.$mobile),6);
        try {
            $lock->block(4);
            $contact = $this->contactMapper->findByRobotIdAndUserId($robotInfo->id,$sender);
            if(empty($contact)){
                return;
            }
            $mobileList = ChContactMobile::query()->where('contact_id', $contact->id)->pluck('mobile')->toArray();
            if(count($mobileList) < 5 && !in_array($mobile,$mobileList)){
                $mobileList[] = $mobile;

                $labelList = $contact->corpTags()->get();

                $labelList = $labelList->map(function ($item) {
                    return [
                        'group_id' => $item['group_id'],
                        'label_id' => $item['id'],
                        'label_type' => $item['label_type'],
                        'owner_id' => $item['owner_id']
                    ];
                })->toArray();
                $robotApiService = make(Robot::class, [$robotInfo->server_id, $robotInfo->uuid])->gateway();
                $remark = empty($contact->remark) ? '' : $contact->remark;
                $desc = empty($contact->desc) ? '' : $contact->desc;
                $company_remark  = empty($contact->company_remark) ? '' : $contact->company_remark;
                $robotApiService->updateWxContact($contact->user_id,$remark,$desc,$company_remark,$mobileList,$labelList);
                $this->contactMapper->rmCache($robotInfo->id,$sender);
            }
        } catch (LockTimeoutException $e) {
            // 无法获取锁...
        } finally {
            optional($lock)->release();
        }
    }

    /**
     * 外部联系人信息变动通知
     * @param array $data
     * @return void
     */
    public function handleExternalContactUpdate(array $data,ChRobot $robotInfo): void
    {
//        $this->friendApply($robotInfo);

        $businessId = 1;

        $key = $this->getDataSyncKey($businessId,$robotInfo->id);
        $currentMaxSeq = $this->getExternalContactSeq($businessId,$robotInfo->id);
        $robotApiService = make(Robot::class, [$robotInfo->server_id, $robotInfo->uuid])->gateway();

        do{
            $result = $robotApiService->dataSync(1,$currentMaxSeq,100);
            if($result === false || empty($result['contact_list'])){
                return;
            }

            $contactList = $result['contact_list'];
            $currentMaxSeq = collect($contactList)->max('seq');
            redis()->set($key,$currentMaxSeq);

            foreach ($contactList as $item){
                $userId = (string)$item['user_id'];
                $info = $this->contactMapper->findByRobotIdAndUserId($robotInfo->id,$userId);
                if(empty($info)){
                    continue;
                }
                $this->updateContact($item,$info);
                //延迟3秒触发标签sop
                co(function () use ($userId,$robotInfo) {
                    Coroutine::sleep(3);
                    $info = $this->contactMapper->findByRobotIdAndUserId($robotInfo->id,$userId);
                    $this->eventDispatcher->dispatch(new SopTagTrigger($info));
                });
            }

            $hasMore = $result['has_more'];
            Coroutine::sleep(1);
        }while($hasMore);
    }

    public function friendApply(ChRobot $robotInfo)
    {
        $businessId = 2;
        $key = $this->getDataSyncKey($businessId,$robotInfo->id);
        $seq = $this->getExternalContactSeq($businessId,$robotInfo->id);
        $robotApiService = make(Robot::class, [$robotInfo->server_id, $robotInfo->robot_key])->gateway();

        do{
            $result = $robotApiService->dataSync($businessId,$seq);
            if($result === false || empty($result['contact_list'])){
                return;
            }

            $contactList = $result['contact_list'];
            $currentMaxSeq = collect($contactList)->max('seq');
            redis()->set($key,$currentMaxSeq);

            foreach ($contactList as $item){
                $item['sender'] = (string)$item['user_id'];
                $this->saveNewContact($item,$robotInfo);
            }

            $hasMore = $result['has_more'];
        }while($hasMore);
    }

    public function handleExternalContactApply(array $data,ChRobot $robotInfo)
    {
        $businessId = 2;
        $key = $this->getDataSyncKey($businessId,$robotInfo->id);
        $seq = $this->getExternalContactSeq($businessId,$robotInfo->id);
        $robotApiService = make(Robot::class, [$robotInfo->server_id, $robotInfo->robot_key])->gateway();
        do{
            $result = $robotApiService->dataSync($businessId,$seq);
            if($result === false || empty($result['contact_list'])){
                return;
            }

            $contactList = $result['contact_list'];
            $currentMaxSeq = collect($contactList)->max('seq');
            redis()->set($key,$currentMaxSeq);

            foreach ($contactList as $item){
                co(function ()use($item,$robotInfo){
                    redis()->set(self::FRIEND_APPLY_KEY.$item['user_id'],1,30 * 86400);
                    $res = container()->get(ChAutoAcceptFriendService::class)->AutoAcceptFriend($robotInfo,$item);
                });
            }

            $hasMore = $result['has_more'];
        }while($hasMore);
    }

    private function updateContact($item,$info)
    {
        $avatar = $item['avatar_url'];

        $labelList = $item['label_list'];
        if(!empty($labelList)){
            $labelList = array_column($labelList, 'label_id');
        }

        $nickname = $item['nickname'];
        $desc = $item['desc'];
        $remark = $item['remark'];
        $companyRemark = $item['company_remark'];

        $contactId = $info->id;
        $robotId = $info->robot_id;
        $userId = $info->user_id;
        if($item['contact_type'] == 0){
            //删除好友
            $info->corpTags()->delete();
            $info->smartTags()->delete();
            ChContactMobile::query()
                ->where('contact_id',$contactId)
                ->delete();
            $info->delete();
            $this->contactMapper->rmCache($robotId,$userId);
        }else{
            $updateData['avatar'] = $avatar;
            $updateData['current_seq'] = $item['seq'];
            $updateData['create_seq'] = $item['create_seq'];
//            $updateData['apply_source'] = $item['apply_source'];
            //来源
            $contactSourceList = ContactSourceConstants::getConstants();
            if (!empty($item['source_info']) && $info->source == 0) {
                $addSource = $item['source_info']['add_source'] ?? 0;
                !in_array($addSource, $contactSourceList) && $addSource = 0;
                $updateData['source'] = $addSource;
            }

            $updateData['source_info'] = $item['source_info'];

            $status = ContactStatusConstants::getStatus($item['contact_type']);
            $updateData['status'] = $status;
            if($status == ContactStatusConstants::LOST_FRIEND || $status == ContactStatusConstants::BLOCKED_FRIEND){
                //好友流失
                $updateData['friend_loss_time'] = Carbon::now()->toDateTimeString();
            }else {
                $updateData['friend_loss_time'] = null;
            }

            //企业标签
            $corpTagIdList = $info->corpTags()->pluck('corp_tag_id')->toArray();
            if(empty($labelList)){
                //删除标签
                if(!empty($corpTagIdList)){
                    $info->corpTags()->detach($corpTagIdList);
                    container()->get(ChCorpTagService::class)->updateCorpTagContactCnt([$contactId],[],$corpTagIdList);
                }
            }else{
                //同步标签
                $rst = $info->corpTags()->sync($labelList);
                $addTagIds = [];
                if(!empty($rst['attached'])){
                    $addTagIds = $rst['attached'];
                }

                $removeTagIds = [];
                if(!empty($rst['detached'])){
                    $removeTagIds = $rst['detached'];
                }
                if(!empty($addTagIds) || !empty($removeTagIds)){
                    container()->get(ChCorpTagService::class)->updateCorpTagContactCnt([$contactId],$addTagIds,$removeTagIds);
                }
            }

            $now =  Carbon::now()->toDateTimeString();

            if(empty($info->nickname) || $nickname != $info->nickname){
                //修改昵称
                $updateData['nickname'] = $nickname;
                $logData[] = [
                    'contact_id' => $contactId,
                    'remark' => "昵称修改为【{$nickname}】",
                    'type' => ContactBehaviorTypeConstants::OTHER,
                    'created_at' => $now,
                ];
            }

            if(empty($info->remark) || $remark != $info->remark){
                //修改备注
                $updateData['remark'] = $remark;
                if(!empty($remark)){
                    $logData[] = [
                        'contact_id' => $contactId,
                        'remark' => "备注修改为【{$remark}】",
                        'type' => ContactBehaviorTypeConstants::OTHER,
                        'created_at' => $now,
                    ];
                }
            }

            if(empty($info->desc) || $desc != $info->desc){
                //修改描述
                $updateData['desc'] = $desc;
                if(!empty($desc)){
                    $logData[] = [
                        'contact_id' => $contactId,
                        'remark' => "描述修改为【{$desc}】",
                        'type' => ContactBehaviorTypeConstants::OTHER,
                        'created_at' => $now,
                    ];
                }
            }

            if(empty($info->company_remark) || $companyRemark != $info->company_remark){
                //修改公司备注
                $updateData['company_remark'] = $companyRemark;
                if(!empty($companyRemark)){
                    $logData[] = [
                        'contact_id' => $contactId,
                        'remark' => "公司备注修改为【{$companyRemark}】",
                        'type' => ContactBehaviorTypeConstants::OTHER,
                        'created_at' => $now,
                    ];
                }
            }

            //修改手机号
            $phoneList = $item['mobile_remark'];
            if(empty($phoneList)){
                $exits = ChContactMobile::query()
                    ->where('contact_id',$contactId)
                    ->exists();
                if($exits){
                    ChContactMobile::query()
                        ->where('contact_id',$contactId)
                        ->delete();
                }
            }else{
                $exits = ChContactMobile::query()
                    ->where('contact_id',$contactId)
                    ->exists();
                if($exits){
                    ChContactMobile::query()
                        ->where('contact_id',$contactId)
                        ->delete();
                }
                $contactMobileData = [];
                foreach ($phoneList as $phone){
                    $contactMobileData[] = [
                        'contact_id' => $contactId,
                        'mobile' => $phone
                    ];
                }
                if(!empty($contactMobileData)){
                    ChContactMobile::insert($contactMobileData);
                }
            }

            if(!empty($updateData)){
                $this->contactMapper->update($contactId,$updateData);
            }

            if(!empty($logData)){
                ChContactBehaviorLog::insert($logData);
            }

            $this->contactMapper->rmCache($robotId,$userId);
        }
    }

    /**
     * 获取数据同步的key
     * @param int $businessId
     * @param int $robotId
     * @return string
     */
    private function getDataSyncKey(int $businessId,int $robotId): string
    {
        if($businessId == 13){
            return 'system_notice_data_sync:'.$robotId.':1';
        }else{
            return 'system_notice_data_sync:'.$robotId.':2';
        }
    }

    private function getExternalContactSeq(int $businessId,int $robotId)
    {
        $seq = redis()->get($this->getDataSyncKey($businessId,$robotId));
        return empty($seq) ? 0 : $seq;
    }
}